﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using ZaidimasSandelys.Models.Classes;
using ZaidimasSandelys.Models.DataStructures;

namespace ZaidimasSandelys.CustomDrawTools
{
    /// <summary>
    /// Žaidimo aikštelė.
    /// Canvas klasės plėtinys, praplečiant jos funkcionalumą.
    /// </summary>
    public class DrawingCanvas: Canvas, IList<AllocatedVisual>
    {
        /// <summary>
        /// WPF įvykis kurį pajungsime XAMLe.
        /// <see cref="http://msdn.microsoft.com/en-us/library/ms752288.aspx"/>
        /// </summary>
        public static readonly RoutedEvent GameOverEvent = EventManager.RegisterRoutedEvent(
            "GameOver", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DrawingCanvas));
        public static readonly int STANDARD_WIDTH = 10;

        public event RoutedEventHandler GameOver
        {
            add { AddHandler(GameOverEvent, value); }
            remove { RemoveHandler(GameOverEvent, value); }
        }
        void RaiseGameOverEvent()
        {
            RoutedEventArgs newEventArgs = new RoutedEventArgs(DrawingCanvas.GameOverEvent);
            RaiseEvent(newEventArgs);
        }

        /// <summary>
        /// Visi žaidimo elementai: sienos, grindys, dėžės, ...
        /// </summary>
        private List<AllocatedVisual> _visuals = new List<AllocatedVisual>();

        /// <summary>
        /// Vietoj <code>Canvas</code> metodo naudosim sąrašo metodą
        /// </summary>
        protected override int VisualChildrenCount
        {
            get
            {
                return this._visuals.Count;
            }
        }

        /// <summary>
        /// Išvalo ankstesnio žaidimo elementus
        /// </summary>
        public void ClearGame()
        {
            for (int i = _visuals.Count - 1; i >= 0; i--)
                DeleteVisual(_visuals[i]);
            _visuals = new List<AllocatedVisual>();
            base.Children.Clear();
        }

        /// <summary>
        /// Žodyne raktu naudojamas <code>System.Drawing.Point</code> kadangi double tipo koordinatės
        /// struktūroje <code>System.Windows.Point</code> negarantuoja tikslaus sulyginimo.
        /// </summary>
        public void RemoveOverloads()
        {
            var tmp = new Dictionary<long, AllocatedVisual>();
            Action<AllocatedVisual> addToDictionary = delegate(AllocatedVisual av)
                {
                    var key = av.Position.DictionaryKey;
                    if (av.ImgType == VisualType.OuterSpace || !tmp.ContainsKey(key))
                        tmp.Add(key, av);
                    else
                        tmp[key] = av;
                };
            
            _visuals.Where(x => !x.CanMove).OrderBy(x => (int)x.ImgType).ForEach(x => addToDictionary(x));
            var rzlt = tmp.Values.ToList();
            rzlt.AddRange(_visuals.Where(x => x.CanMove));

            _visuals = rzlt;
        }

        /// <summary>
        /// Kordinačių perskaičiavimo konstantos
        /// </summary>
        private Size _multiplicator = new Size (20.0, 20.0);

        /// <summary>
        /// Vietoj <code>Canvas</code> metodo naudosim sąrašo metodą ir grąžinsim <code>AllocatedVisual</code>
        /// </summary>
        protected override Visual GetVisualChild(int index)
        {
            return this._visuals[index];
        }

        /// <summary>
        /// Surasti elementą pagal jo kairio viršutinio kampo koordinates
        /// </summary>
        /// <param name="p">Kairio viršutionio kampo koordinatės</param>
        /// <returns>
        ///     Elementą, kurio kairio viršutinio kampo koordinatės sutampa su parametro reikšmėm 
        ///     arba NULLą jei tokio elemento nėra
        /// </returns>
        public IList<AllocatedVisual> GetVisualChild(Movement p)
        {
            return this._visuals.Where(x => x.Position == p).ToList();
        }

        public void AddVisual(AllocatedVisual visual)
        {
            this._visuals.Add(visual);
            ConnectContainer(visual);
        }

        private void AddVisual(AllocatedVisual visual, bool withRemoving)
        {
            if (withRemoving && AllocatedVisual.SinglePointElements.Contains(visual.ImgType))
            {
                IList<AllocatedVisual> anotherElements = this.GetVisualChild(visual.Position);
                if (anotherElements != null)
                {
                    for (int i = anotherElements.Count - 1; i >= 0; i--)
                        DeleteVisual(anotherElements[i]);
                }
            }
            AddVisual(visual);
        }

        public void DeleteVisual(AllocatedVisual visual)
        {
            this._visuals.Remove(visual);

            base.RemoveVisualChild(visual);
            base.RemoveLogicalChild(visual);

            visual = null;
        }

        /// <summary>
        /// Išrūšiuoti pagal prioritetą elementai kraunami į žaidimą. Prioritetai:
        /// <list type="bullet">
        ///     <item>0 - tuscia erdve</item>
        ///     <item>1 - grindys</item>
        ///     <item>2 - target</item>
        ///     <item>3 - siena</item>
        ///     <item>4 - dezes</item>
        ///     <item>5 - zmogus</item>
        /// </list>
        /// </summary>
        public void DrawContent()
        {
            Action<AllocatedVisual> drawElement = delegate(AllocatedVisual visual)
            {
                visual.DrawItself(_multiplicator);
                base.AddVisualChild(visual);
                base.AddLogicalChild(visual);
            };

            double columnCount = this._visuals.Where(el => el.Position.Height == 0).Max(el => el.Position.Width) + 1;
            double rowCount = this._visuals.Where(el => el.Position.Width == 0).Max(el => el.Position.Height) + 1;
            _multiplicator.Height = _multiplicator.Width = 
                    Math.Min(this.ActualWidth / columnCount, this.ActualHeight / rowCount);
            _visuals.OrderBy(x => (short)x.ImgType).ForEach(drawElement);
        }

        private void ConnectContainer(AllocatedVisual visual)
        {
            if (visual.ImgType == VisualType.Container || visual.ImgType == VisualType.HeavyContainer)
            {
                var container = (ContainerVisual)visual;
                container.AllGameElements = this;
                if (visual.ImgType == VisualType.Container)
                    container.MovedOnTarget += container_MovedOnTarget;
                else
                    ((HeavyContainerVisual)visual).MovedOnTarget += container_MovedOnTarget;
            }
        }

        public void container_MovedOnTarget(object sender, EventArgs e)
        {
            bool isNotReadyContainer = _visuals
                .Where(z => z.ImgType == VisualType.Container || z.ImgType == VisualType.HeavyContainer)
                .Cast<ContainerVisual>()
                .Any(x => !x.IsContainerOnTarget);
            if (!isNotReadyContainer)
                RaiseGameOverEvent();
        }

        #region IList<AllocatedVisual> implementacija. Reikalinga testams.

        public int IndexOf(AllocatedVisual item)
        {
            return _visuals.IndexOf(item);
        }
        public void Insert(int index, AllocatedVisual item)
        {
            _visuals.Insert(index, item);
        }
        public void RemoveAt(int index)
        {
            _visuals.RemoveAt(index);
        }
        public AllocatedVisual this[int index]
        {
            get { return _visuals[index]; }
            set { _visuals[index] = value; }
        }
        public void Add(AllocatedVisual item)
        {
            this.AddVisual(item, true);
            ConnectContainer(item);
        }
        public void Clear()
        {
            _visuals.Clear();
        }
        public bool Contains(AllocatedVisual item)
        {
            return _visuals.Contains(item);
        }
        public void CopyTo(AllocatedVisual[] array, int arrayIndex)
        {
            _visuals.CopyTo(array, arrayIndex);
        }
        public int Count
        {
            get { return _visuals.Count; }
        }
        public bool IsReadOnly
        {
            get { return false; }
        }
        public bool Remove(AllocatedVisual item)
        {
            return _visuals.Remove(item);
        }
        public IEnumerator<AllocatedVisual> GetEnumerator()
        {
            return _visuals.GetEnumerator();
        }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (System.Collections.IEnumerator)_visuals.GetEnumerator();
        }

        #endregion
    }
}
